﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

using SharedGenomics.Core;
using SharedGenomics.Annotations;

namespace SharedGenomics.Workbench.Annotations
{
    /// <summary>
    /// The AnnotationManager is used to control which annotations are active in the workbench i.e. which annotations will be fetched and shown to the user
    /// </summary>
    public class AnnotationManager : IAnnotationManager
    {
        private readonly IDictionary<AnnotationDescriptor, bool> _annotations;
        private readonly IDictionary<Type, AnnotationDescriptor> _descriptorTypes;
        private readonly ActiveAnnotationsPersistanceManager _preferencesManager;

        public AnnotationManager(ActiveAnnotationsPersistanceManager preferencesManager)
        {
            this._preferencesManager = preferencesManager;

            //load user preferences and create a mappings for:
            //- a descriptor to it's activation status
            //- an annotation type to the corresponding descriptor
            this._annotations = this._preferencesManager.Load().ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
            this._descriptorTypes = this._annotations.Keys.ToDictionary(ad => ad.AnnotationType, ad => ad);
        }

        public void ActivateAll()
        {
            this.SetAllActivations(true);
        }

        public void DeactivateAll()
        {
            this.SetAllActivations(false);
        }

        private void SetAllActivations(bool active)
        {
            //NOTE: need to copy to list otherwise it complains that the collection is being modified during enumeration
            foreach (var key in this._annotations.Keys.ToList())
            {
                this._annotations[key] = active;
            }
        }

        public IEnumerable<AnnotationDescriptor> ActiveAnnotationDescriptors
        {
            get { return this._annotations.Where(kvp => kvp.Value).Select(kvp => kvp.Key); }
        }

        public IEnumerable<KeyValuePair<AnnotationDescriptor, bool>> AnnotationActivationStatuses
        {
            get { return this._annotations.AsEnumerable(); }
        }

        public IEnumerable<Type> ActiveAnnotationTypes
        {
            get { return this.ActiveAnnotationDescriptors.Select(ad => ad.AnnotationType); }
        }

        public void SetAnnotationActive(Type annotationType, bool active)
        {
            annotationType.ThrowIfNull("annotationType");
            AnnotationDescriptor descriptor = this.GetDescriptorForType(annotationType);
            this._annotations[descriptor] = active;
        }

        public bool GetAnnotationActivation(Type annotationType)
        {
            annotationType.ThrowIfNull("annotationType");
            AnnotationDescriptor descriptor = this.GetDescriptorForType(annotationType);

            Debug.Assert(this._annotations.ContainsKey(descriptor));
            return this._annotations[descriptor];
        }

        /// <summary>
        /// Saves the current state of the AnnotationManager i.e. which annotations are active
        /// </summary>
        public void Save()
        {
            this._preferencesManager.Save(this.ActiveAnnotationTypes);
        }

        private AnnotationDescriptor GetDescriptorForType(Type type)
        {
            Debug.Assert(type != null);
            ExceptionHelper.ThrowIf<ArgumentException>(!typeof(IAnnotation).IsAssignableFrom(type));
            ExceptionHelper.ThrowIf<ArgumentException>(!this._descriptorTypes.ContainsKey(type), String.Format("Could not find descriptor for annotation type {0}", type.FullName));

            return this._descriptorTypes[type];
        }
    }
}
